home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’97 / Keyed Menus / source code / INIT.c next >
Encoding:
C/C++ Source or Header  |  1997-06-27  |  9.9 KB  |  411 lines  |  [TEXT/KAHL]

  1. #include <Events.h>
  2. #include <Resources.h>
  3. #include <Memory.h>
  4. #include <ToolUtils.h>
  5. #include <Menus.h>
  6.  
  7. #define CurMenuID   (*(short *)0xB54)
  8. #define CurMenuItem (*(short *)0xB56)
  9.  
  10. #include "Appearance.h"
  11.  
  12. /* Character Codes */
  13. enum
  14. {
  15.     kEnterKey                = 0x03,
  16.     kReturnKey                = 0x0D,
  17.     kEscapeKey                = 0x1B,
  18.     kLeftArrowKey            = 0x1C,
  19.     kRightArrowKey            = 0x1D,
  20.     kUpArrowKey                = 0x1E,
  21.     kDownArrowKey            = 0x1F,
  22.     kSpaceKey                = 0x20,
  23.     kDeleteKey                = 0x08,
  24.     kTabKey                    = 0x09,
  25.     kPeriodKey                = '.'
  26. };
  27.  
  28. // Need to patch: MenuSelect, WaitMouseUp, GetMouse, SystemEvent
  29.  
  30. #ifndef powerc
  31.     #ifdef THINK_C
  32.         #pragma parameter __D0 SetA4(__D0)
  33.         pascal long SetA4(long newA4) = 0xC18C;
  34.     
  35.         #define SetCurrentA4() SetA4((long)&main)
  36.     #else
  37.         #include <A4Stuff.h>
  38.     //    #include <SetUpA4.h>
  39.     #endif
  40. #else
  41.     #define SetCurrentA4() 0
  42.     #define SetA4(x) 0
  43.  
  44.     ProcInfoType __procinfo = kCStackBased
  45.              | RESULT_SIZE(kFourByteCode)
  46.              | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(void *)));
  47. #endif
  48.  
  49. pascal long (*gOldMenuSelect)(Point pt);
  50. pascal long myMenuSelect(Point pt);
  51. enum {
  52.     uppMenuSelectProcInfo = kPascalStackBased | RESULT_SIZE(kFourByteCode) | STACK_ROUTINE_PARAMETER(1, kFourByteCode)
  53. };
  54.  
  55.  
  56. pascal short (*gOldWaitMouseUp)(void);
  57. pascal short myWaitMouseUp(void);
  58. enum {
  59.     uppWaitMouseUpProcInfo = kPascalStackBased | RESULT_SIZE(kTwoByteCode)
  60. };
  61.  
  62. pascal void (*gOldGetMouse)(Point *ptp);
  63. pascal void myGetMouse(Point *ptp);
  64. enum {
  65.     uppGetMouseProcInfo = kPascalStackBased | STACK_ROUTINE_PARAMETER(1, kFourByteCode)
  66. };
  67.  
  68. pascal short (*gOldSystemEvent)(EventRecord *theEvent);
  69. pascal short mySystemEvent(EventRecord *theEvent);
  70. enum {
  71.     uppSystemEventProcInfo = kPascalStackBased | RESULT_SIZE(kTwoByteCode) | STACK_ROUTINE_PARAMETER(1, kFourByteCode)
  72. };
  73.  
  74. enum {
  75.     kIdle = 0,
  76.     kInMenuSelect = 1
  77. } gMenuKeyState = kIdle;
  78.  
  79. int gNeedItemRedraw;
  80. static short gHaveFakeMSResult = false;
  81. static long gFakeMSResult;
  82.  
  83. void main(void)
  84. {
  85.     long    oldA4;
  86.     THz        oldZone;
  87.  
  88.         // Set up A4, so we can access our globals.
  89.     oldA4 = SetCurrentA4();
  90.  
  91.         // Set the current zone to the system zone.  In the 680x0 case, this
  92.         // is not necessary, but it's not a bad idea and it keeps us out of
  93.         // trouble when traps that we don't expect to have side effects
  94.         // unexpectedlty allocate memory from the current zone.  One example
  95.         // of this is the NewRoutineDescriptor routine.
  96.     oldZone = GetZone();
  97.     SetZone(SystemZone());
  98.  
  99.         // We need to detach our code, so that we stay around.
  100.     DetachResource(GetResource('INIT', 237));
  101.  
  102.         // Remember the old implementation of WaitMouseUp and MenuSelect and GetMouse and SystemEvent.
  103.     gOldWaitMouseUp = (void *) GetToolTrapAddress(0xA977);
  104.     gOldGetMouse = (void *) GetToolTrapAddress(0xA972);
  105.     gOldSystemEvent = (void *) GetToolTrapAddress(0xA9B2);
  106.     gOldMenuSelect = (void *) GetToolTrapAddress(0xA93D);
  107.  
  108.         // Patch ourselves in.
  109.     SetToolTrapAddress(NewRoutineDescriptor((ProcPtr) &myWaitMouseUp,uppWaitMouseUpProcInfo,kPowerPCISA), 0xA977);
  110.     SetToolTrapAddress(NewRoutineDescriptor((ProcPtr) &myGetMouse,uppGetMouseProcInfo,kPowerPCISA), 0xA972);
  111.     SetToolTrapAddress(NewRoutineDescriptor((ProcPtr) &mySystemEvent,uppSystemEventProcInfo,kPowerPCISA), 0xA9B2);
  112.     SetToolTrapAddress(NewRoutineDescriptor((ProcPtr) &myMenuSelect,uppMenuSelectProcInfo,kPowerPCISA), 0xA93D);
  113.  
  114.         // Restore the old zone again
  115.     SetZone(oldZone);
  116.  
  117.         // And restore the value of A4 on the way out.
  118.     SetA4(oldA4);
  119. }
  120.  
  121. /*------------------------------------------------------------------
  122.     ConvertKeyToChar        [internal]
  123.     
  124.     This function uses international utilities to convert a specific
  125.     key code from the keyboard to a character given the current script.
  126.     
  127.     Parameters:
  128.         In:        inKeyCode        - key code for current keyboard
  129.         Out:    (return)        - associated char code
  130. ------------------------------------------------------------------*/
  131.  
  132. static UInt8
  133. ConvertKeyToChar(UInt8 inKeyCode) 
  134. {
  135.     static SInt16    sPrevKchrID = 237;
  136.     static UInt8    sCharsOfKeys[256];
  137.     SInt16            curKchrID;
  138.     UInt8            charCode;
  139.     UInt32            zero = 0;
  140.  
  141.     curKchrID = GetScriptVariable(smCurrentScript, smScriptKeys);
  142.  
  143.     // If this is the first time through here or if the
  144.     // script has changed since we last executed this, we
  145.     // need to update our key-to-char code mapping.
  146.     if (curKchrID != sPrevKchrID)
  147.     {
  148.         Handle            kchrRsrc;
  149.         UInt16            keyCode;
  150.         
  151.         kchrRsrc = GetResource('KCHR', curKchrID);
  152.         if (kchrRsrc == NULL)
  153.             kchrRsrc = GetIndResource('KCHR', 1);
  154.         
  155.         if (kchrRsrc == NULL) DebugStr("\pkchrRsrc == NULL");
  156.  
  157.         for (keyCode = 0; keyCode <= 255; keyCode++)
  158.         {
  159.             sCharsOfKeys[keyCode] = (keyCode <= 127 ? 
  160.                         KeyTranslate(*kchrRsrc, keyCode, &zero) : -1);
  161.         }
  162.  
  163.         sPrevKchrID = curKchrID;
  164.     }
  165.  
  166.     // Now that we have a valid mapping array, the translation
  167.     // is as simple as an array lookup.
  168.     charCode = sCharsOfKeys[(UInt8)inKeyCode];
  169.  
  170.     // Convert from lower-case to upper-case if alpha character
  171.     if (charCode >= 'a' && charCode <= 'z')
  172.         charCode += 'A' - 'a';
  173.  
  174.     return charCode;
  175. }
  176.  
  177. static void 
  178. GetKeyASCII(    UInt8         charCode, 
  179.                 UInt8         keyCode, 
  180.                 UInt8*        outText) 
  181. {
  182.     const static UInt8         sStringLookupArray[] = {
  183.         0x01, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x1B, 0x1B,
  184.         0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x7F,
  185.         0x7A, 0x78, 0x63, 0x76, 0x60, 0x61, 0x62, 0x64, 0x65, 0x6D, 0x67,
  186.         0x6F, 0x69, 0x6B, 0x71
  187.     };
  188.     
  189.     // Convert the key code to a character using the current script
  190.     charCode = ConvertKeyToChar(keyCode);
  191.  
  192.     // If we are in the range of standard characters, just use the character
  193.     if (1) // if (charCode > kSpaceKey && charCode <= 0x7E) 
  194.     {
  195.         if (1) // if (keyCode < 0x40) 
  196.         {
  197.             // Standard keys
  198.             outText[0] = 1;
  199.             outText[1] = charCode;
  200.         }
  201.         else 
  202.         {
  203.             // Keypad keys
  204.             outText[0] = 3;
  205.             outText[1] = '[';
  206.             outText[2] = charCode;
  207.             outText[3] = ']';
  208.         }
  209.     } 
  210.     else
  211.     {
  212.         UInt16                    itemIndex;
  213.         const UInt8*            lookup;
  214.  
  215.         // Special keys which are represented by strings in a resource
  216.         lookup = sStringLookupArray;
  217.  
  218.         if (charCode == kEscapeKey) 
  219.         {
  220.             // Multiple keys map to the escape char code
  221.             itemIndex = (keyCode <= 0x40) ? 11 : 12;
  222.         } 
  223.         else 
  224.         {
  225.             // Scan the array for a match with the char code or key code
  226.             for (itemIndex = 2; itemIndex <= 33; itemIndex++, lookup++) 
  227.             {
  228.                 if (itemIndex <= 18)
  229.                 {
  230.                     if (charCode == *lookup)
  231.                         break;
  232.                 } 
  233.                 else 
  234.                 {
  235.                     if (keyCode == *lookup) 
  236.                         break;
  237.                 }
  238.             }
  239.             
  240.             if (itemIndex > 33) 
  241.                 itemIndex = 1;
  242.         }
  243.  
  244. //        GetIndString(outText, kKeyStrId, itemIndex);
  245.     }
  246. }
  247.  
  248. static void TranslateEvent(short emodifiers, long emessage, short *modifiersp, short *ASCII)
  249. {
  250.     short    modifiers = 0;
  251.     if (!(emodifiers & cmdKey))  modifiers |= kMenuNoCommandModifier;
  252.     if (emodifiers & shiftKey)   modifiers |= kMenuShiftModifier;
  253.     if (emodifiers & optionKey)  modifiers |= kMenuOptionModifier;
  254.     if (emodifiers & controlKey) modifiers |= kMenuControlModifier;
  255.     *modifiersp = modifiers;
  256.     *ASCII = ConvertKeyToChar((char) (emessage >> 8));
  257. }
  258.  
  259. static int IsValidKeyEvent(EventRecord *erp) {
  260.     char    ASCIIval = erp->message;
  261.  
  262.     if (erp->modifiers & (cmdKey || optionKey || controlKey)) return true;
  263.     if (ASCIIval < ' ' || ASCIIval > 0x7F) return true;
  264.     return false;
  265. }
  266.  
  267.  
  268. pascal short myWaitMouseUp(void)
  269. {
  270.     long        oldA4;
  271.     short        result;
  272.     long        temp;
  273.     short        menuID, menuItem;
  274.     short        menuModifiers, menuASCII;
  275.     MenuHandle    mh;
  276.     EventRecord    keyEvent;
  277.  
  278.         // Set up A4, so we can access our globals.
  279.     oldA4 = SetCurrentA4();
  280.  
  281.     if (gMenuKeyState == kInMenuSelect) {
  282.         menuID   = CurMenuID;
  283.         menuItem = CurMenuItem;
  284.  
  285.         if (menuID && menuItem) {
  286.             mh = GetMHandle(menuID);
  287.             if (mh && ((**mh).enableFlags >> menuItem) & 1) {
  288.                 if (GetOSEvent(keyDownMask, &keyEvent) && IsValidKeyEvent(&keyEvent)) {
  289.                     if (kDeleteKey == (char)keyEvent.message) {
  290.                         SetItemCmd(mh, menuItem, 0);
  291.                         SetMenuItemModifiers(mh, menuItem, 0);
  292.                         gNeedItemRedraw = 5;
  293.                     } else {
  294.                         TranslateEvent(keyEvent.modifiers, keyEvent.message, &menuModifiers, &menuASCII);
  295.                         SetItemCmd(mh, menuItem, menuASCII);
  296.                         SetMenuItemModifiers(mh, menuItem, menuModifiers);
  297.                         gNeedItemRedraw = 5;
  298.                     }
  299.                 }
  300.             }
  301.         }
  302.     }
  303.  
  304.         // Call the old WaitMouseUp:
  305. #ifndef powerc
  306.     result = gOldWaitMouseUp();
  307. #else
  308.     result = CallUniversalProc((UniversalProcPtr)gOldWaitMouseUp, uppWaitMouseUpProcInfo);
  309. #endif
  310.  
  311.         // And restore the value of A4 on the way out.
  312.     SetA4(oldA4);
  313.  
  314.     return result;
  315. }
  316.  
  317. pascal void myGetMouse(Point *ptp)
  318. {
  319.     long        oldA4;
  320.  
  321.         // Set up A4, so we can access our globals.
  322.     oldA4 = SetCurrentA4();
  323.  
  324.     if (gNeedItemRedraw) {
  325.         gNeedItemRedraw --;
  326.         ptp->h = 0x0237;
  327.         ptp->v = 0x0237;
  328.     } else {
  329.             // Call the old GetMouse:
  330. #ifndef powerc
  331.         gOldGetMouse(ptp);
  332. #else
  333.         CallUniversalProc((UniversalProcPtr)gOldGetMouse, uppGetMouseProcInfo, ptp);
  334. #endif
  335.     }
  336.  
  337.         // And restore the value of A4 on the way out.
  338.     SetA4(oldA4);
  339. }
  340.  
  341. pascal short mySystemEvent(EventRecord *erp)
  342. {
  343.     long        oldA4;
  344.     short        result;
  345.     long        fakeMS;
  346.     char        ASCIIval;
  347.  
  348.         // Set up A4, so we can access our globals.
  349.     oldA4 = SetCurrentA4();
  350.  
  351.     if (erp->what == keyDown || erp->what == autoKey)
  352.     {
  353.         ASCIIval = erp->message;
  354.         if (IsValidKeyEvent(erp))
  355.         {
  356.             gFakeMSResult = MenuEvent(erp);
  357.             if ((gFakeMSResult & 0xFFFF0000) && (gFakeMSResult & 0xFFFF)) {
  358.                 gHaveFakeMSResult = true;
  359.                 erp->what = mouseDown;
  360.                 erp->where.h = 37;
  361.                 erp->where.v = 2;
  362.             } else
  363.                 gHaveFakeMSResult = false;
  364.         }
  365.     }
  366.  
  367.         // Call the old SystemEvent:
  368. #ifndef powerc
  369.     result = gOldSystemEvent(erp);
  370. #else
  371.     result = CallUniversalProc((UniversalProcPtr)gOldSystemEvent, uppSystemEventProcInfo, erp);
  372. #endif
  373.  
  374.         // And restore the value of A4 on the way out.
  375.     SetA4(oldA4);
  376.  
  377.     return result;
  378. }
  379.  
  380. pascal long myMenuSelect(Point pt)
  381. {
  382.         // Set up A4, so we can access our globals.
  383.     long    oldA4;
  384.     long    result;
  385.     int        oldState;
  386.  
  387.     oldA4 = SetCurrentA4();
  388.  
  389.     oldState = gMenuKeyState;
  390.     gMenuKeyState = kInMenuSelect;
  391.  
  392.     if (gHaveFakeMSResult) {
  393.         gHaveFakeMSResult = false;
  394.         result = gFakeMSResult;
  395.     } else {
  396.         // Call the old MenuSelect:
  397. #ifndef powerc
  398.         result = gOldMenuSelect(theEvent);
  399. #else
  400.         result = CallUniversalProc((UniversalProcPtr)gOldMenuSelect, uppMenuSelectProcInfo, pt);
  401. #endif
  402.     }
  403.  
  404.     gMenuKeyState = kIdle;
  405.  
  406.         // And restore the value of A4 on the way out.
  407.     SetA4(oldA4);
  408.  
  409.     return result;
  410. }
  411.